--[[ Copyright 2012, Chris Ho                               ]]--
--[[ MIT License (Please read the LICENSE file for details) ]]--

Use("common/Const");
Use("common/Log");
Use("common/Position");
Use("common/Creature");
Use("common/Skill");
Use("common/Task");

-- Patrol Task (move to destination, but keeping original state)
-- progress: progress state
-- id: who is responsible for the task
-- origin: where the task started (patrol origin)
-- time: when the task started
-- hp: hit points at task creation
-- sp: skill points at task creation
-- resilience: modified resilience value
-- destination: desired waypoint
-- target: nonzero if a target is active
Patrol = {};

function Patrol.Create(_pet, _source, _destination)
  local instance = Task.Create(_pet);

  instance.resilience = _pet:GetResilience();
  instance.skill = Skill.None(_pet.id);
  instance.destination = _destination;
  instance.target = 0;

  --[[ Move to the destination ]]--
  function instance:Execute()
    Debug("Patrol.Execute()");

    if (GetV(V_HP, self.id) < (self.hp / self.resilience)) then
      -- We've taken a lot of damage since the task began. Abandon this
      -- task and try to do something else.
      Info("Patrol.Execute() interrupting task")
      return self:Progress(PROGRESS_FAILURE);
    end

    if (self.target == 0) then
      -- Try to evaluate threats.
      local creature = Creature.Create(self.id);
      local threats = creature:Scan(ACTORS);
      if (threats.n > 0) then
        -- Take the first threat from the list as our target.
        self.target = threats[1];
      end
    end

    if (self.target ~= 0) then
      -- Don't target dead things.
      local motion = GetV(V_MOTION, self.target);
      if ((motion == MOTION_DEAD) or (motion < 0)) then
        Info("Patrol: Target " .. self.target .. " is unavailable.");
        self.target = 0;
      else
        local from = Position.Find(self.id);
        local to = Position.Find(self.target);

        -- If it's too far away, just sit there.
        if (Position.Measure(from, to) >= VISIBILITY) then
          Info("Target out of range. (" .. motion .. ")");
          self.target = 0;
        else
          local range = self.skill:Estimate();

          -- Try to move in range.
          local result = self:Approach(Position.Find(self.target), range);

          -- Not in range yet.
          if (result == PROGRESS_ACTIVE) then return self:Progress(result) end

          -- We should be in range by now. Attack if we're in range.
          if (result == PROGRESS_SUCCESS) then result = self:Progress(self:Target(self.skill, self.target)) end

          -- Attack is pending.
          if (result == PROGRESS_ACTIVE) then return self:Progress(result) end
        end
      end
    end

    -- Resume patrol.
    return self:Progress(self:Move(self.destination));
  end

  return instance;
end